Skip to content

Conversation

captainsafia
Copy link
Member

@captainsafia captainsafia commented Apr 28, 2025

Closes #61525.

  • Replace RequiredSymbols with WellKnownTypeCache
  • Re-use ParsabilityHelper shared code from RDG to detect parsable types

@ghost ghost added the needs-area-label Used by the dotnet-issue-labeler to label those issues which couldn't be triaged automatically label Apr 28, 2025
@captainsafia captainsafia requested a review from Copilot April 28, 2025 20:53
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes the handling of parsable types in the validations generator by adding additional known type data and refactoring the generator code.

  • Added new well-known types (e.g. JsonDerivedTypeAttribute, DataAnnotations attributes) to support enhanced validations.
  • Updated tests and snapshots to cover parsable properties.
  • Refactored generator parsers and extensions to replace RequiredSymbols with WellKnownTypes.

Reviewed Changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/Shared/RoslynUtils/WellKnownTypeData.cs Added new entries to support additional attributes.
src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/... Updated expected test snapshots to reflect changes.
src/Http/Http.Extensions/test/ValidationsGenerator/ValidationsGenerator.Parsable.cs Added tests to validate parsable property handling.
src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.ValidationsGenerator/ValidationsGenerator.cs Simplified generator initialization by removing unnecessary symbols resolution.
Other generator/parsers files Refactored to use WellKnownTypes, ensuring proper resolution of types.
Files not reviewed (1)
  • src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.ValidationsGenerator/Microsoft.AspNetCore.Http.ValidationsGenerator.csproj: Language not supported

@captainsafia captainsafia added area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-validation Issues related to model validation in minimal and controller-based APIs and removed needs-area-label Used by the dotnet-issue-labeler to label those issues which couldn't be triaged automatically labels Apr 28, 2025
// Extract validatable types discovered in members of this type and add them to the top-level list.
var members = ExtractValidatableMembers(typeSymbol, requiredSymbols, ref validatableTypes, ref visitedTypes);
ImmutableArray<ValidatableProperty> members = [];
if (ParsabilityHelper.GetParsability(typeSymbol, wellKnownTypes) is Parsability.NotParsable)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the change in ValidationsGenerator are the only real changes in this PR?

And this is removing TryParseable types from the validatable types? But somehow they are still validatable?

Copy link
Member Author

@captainsafia captainsafia Apr 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the change in ValidationsGenerator are the only real changes in this PR?

Yep, this is the main change. All the WellKnownType stuff is a reaction to the fact that this API needs it.

And this is removing TryParseable types from the validatable types? But somehow they are still validatable?

We'll still generate ValidatablePropertyInfo instances for them so that we'll validate attributes on the properties. This change does mean that we won't validate properties with validation attributes inside the parsable types. None of the properties in the case below get validated. I'd have to check how MVC handles this...if it does...

The right pattern hear might be to require that custom parsable types implement IValidatableObject to handle validation.

public class Person
{
    [Required]
    [StringLength(100, MinimumLength = 2)]
    public string FirstName { get; private set; } = string.Empty;

    [Required]
    [StringLength(100, MinimumLength = 2)]
    public string LastName { get; private set; } = string.Empty;

    [Range(0, 120)]
    public int Age { get; private set; }

    // Static TryParse method
    public static bool TryParse(string input, out Person? person) { }    
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: it looks like MVC doesn't validate properties on a TryParse by default:

#:sdk Microsoft.NET.Sdk.Web
#:property TargetFramework net10.0

using Microsoft.AspNetCore.Mvc;
using System.ComponentModel.DataAnnotations;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.MapControllers();

app.Run();

[ApiController]
[Route("[controller]")]
public class PersonController : ControllerBase
{
    // This endpoint binds PersonInput from query string
    [HttpGet]
    public IActionResult CreatePerson([FromQuery] PersonInput personInput)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        return Ok(new { Message = $"Created person: {personInput.FirstName} {personInput.LastName}, Age {personInput.Age}" });
    }
}

public class PersonInput
{
    [Required]
    [StringLength(100, MinimumLength = 2)]
    public string FirstName { get; init; } = string.Empty;

    [Required]
    [StringLength(100, MinimumLength = 2)]
    public string LastName { get; init; } = string.Empty;

    [Range(0, 120)]
    public int Age { get; init; }

    // TryParse for parsing raw CSV strings (no validation inside)
    public static bool TryParse(string input, out PersonInput? person)
    {
        person = null;

        if (string.IsNullOrWhiteSpace(input))
            return false;

        var parts = input.Split(',', StringSplitOptions.TrimEntries);
        if (parts.Length != 3)
            return false;

        if (!int.TryParse(parts[2], out int age))
            return false;

        person = new PersonInput
        {
            FirstName = parts[0],
            LastName = parts[1],
            Age = age
        };

        return true;
    }
}
$ curl http://localhost:5000/person?personInput=S,A,128
{
  "message": "Created person: S A, Age 128"
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The right pattern hear might be to require that custom parsable types implement IValidatableObject to handle validation.

Does this work now, or are you saying we might want to make this work in the future?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't work now. We would need to add a check to see if the type implements IValidatableObject and if so generate an IValidatableInfo instance for that member type.

@captainsafia captainsafia merged commit 21ba24a into main Apr 30, 2025
26 of 27 checks passed
@captainsafia captainsafia deleted the safia/vg-parsable branch April 30, 2025 22:04
@dotnet-policy-service dotnet-policy-service bot added this to the 10.0-preview5 milestone Apr 30, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc feature-validation Issues related to model validation in minimal and controller-based APIs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix validations generator handling for parsable types

2 participants